#include "vm.hpp"
#include "bitinput.hpp"
#include "debug.hpp"
#include <algorithm>
#include <iostream>
#include <cstring>
#include <boost/crc.hpp>

namespace fur
{
   using std::min;

   namespace VMCmdFlag
   {
      const uint8_t OP0 = 0;
      const uint8_t OP1 = 1;
      const uint8_t OP2 = 1 << 1;
      const uint8_t OPMASK = 3;
      const uint8_t BYTEMODE = 1 << 2;
      const uint8_t JUMP = 1 << 3;
      const uint8_t PROC = 1 << 4;
      const uint8_t USEFLAG = 1 << 5;
      const uint8_t CHFLAG = 1 << 6;
   };

   namespace
   {
      uint8_t vm_cmd_flags[] = 
      {
	 /*MOV*/ VMCmdFlag::OP2 | VMCmdFlag::BYTEMODE,
	 /*CMP*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*ADD*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*SUB*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*JZ*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*JNZ*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*INC*/ VMCmdFlag::OP1|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*DEC*/ VMCmdFlag::OP1|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*JMP*/ VMCmdFlag::OP1|VMCmdFlag::JUMP,
	 /*XOR*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*AND*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*OR*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*TEST*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*JS*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*JNS*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*JB*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*JBE*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*JA*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*JAE*/ VMCmdFlag::OP1|VMCmdFlag::JUMP|VMCmdFlag::USEFLAG,
	 /*PUSH*/ VMCmdFlag::OP1,
	 /*POP*/ VMCmdFlag::OP1,
	 /*CALL*/ VMCmdFlag::OP1|VMCmdFlag::PROC,
	 /*RET*/ VMCmdFlag::OP0|VMCmdFlag::PROC,
	 /*NOT*/ VMCmdFlag::OP1|VMCmdFlag::BYTEMODE,
	 /*SHL*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*SHR*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*SAR*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*NEG*/ VMCmdFlag::OP1|VMCmdFlag::BYTEMODE|VMCmdFlag::CHFLAG,
	 /*PUSHA*/ VMCmdFlag::OP0,
	 /*POPA*/ VMCmdFlag::OP0,
	 /*PUSHF*/ VMCmdFlag::OP0|VMCmdFlag::USEFLAG,
	 /*POPF*/ VMCmdFlag::OP0|VMCmdFlag::CHFLAG,
	 /*MOVZX*/ VMCmdFlag::OP2,
	 /*MOVSX*/ VMCmdFlag::OP2,
	 /*XCHG*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE,
	 /*MUL*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE,
	 /*DIV*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE,
	 /*ADC*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::USEFLAG|VMCmdFlag::CHFLAG,
	 /*SBB*/ VMCmdFlag::OP2|VMCmdFlag::BYTEMODE|VMCmdFlag::USEFLAG|VMCmdFlag::CHFLAG,
	 /*PRINT*/ VMCmdFlag::OP0
      };
   } // end of unamed namespace

   VMPreparedProgram::VMPreparedProgram()
   {
      altcmd = NULL;
      filtereddatasize = 0;
   }

   VisualMachine::VisualMachine()
   {
      mem = new uint8_t[VM_MEMSIZE + 4];
   }

   VisualMachine::~VisualMachine()
   {
      delete [] mem;
   }

   bool VisualMachine::is_vm_mem(uint32_t *addr) const throw()
   {
      return reinterpret_cast<uint8_t*>(addr) >= mem 
	 && reinterpret_cast<uint8_t*>(addr) < mem + VM_MEMSIZE;
   }

   uint32_t VisualMachine::getvalue(bool bytemode, uint32_t *addr)
   {
      if (bytemode)
      {
	 return (*reinterpret_cast<uint8_t*>(addr));
      }
      else
      {
	 if (is_vm_mem(addr))
	 {
	    uint8_t *b = reinterpret_cast<uint8_t*>(addr);
	    return uint32_t(static_cast<uint32_t>(b[0])
			    | static_cast<uint32_t>(b[1]) << 8
			    | static_cast<uint32_t>(b[2]) << 16
			    | static_cast<uint32_t>(b[3]) << 24);
	 }
	 else
	 {
	    return uint32_t(*addr);
	 }
      }
   }

   void VisualMachine::setvalue(bool bytemode, uint32_t *addr, uint32_t value)
   {
      if (bytemode)
      {
	 *reinterpret_cast<uint8_t*>(addr) = value;
      }
      else
      {
	 if (is_vm_mem(addr))
	 {
	    uint8_t *b = reinterpret_cast<uint8_t*>(addr);
	    b[0] = static_cast<uint8_t>(value);
	    b[1] = static_cast<uint8_t>(value >> 8);
	    b[2] = static_cast<uint8_t>(value >> 16);
	    b[3] = static_cast<uint8_t>(value >> 24);
	 }
	 else
	 {
	    *reinterpret_cast<uint8_t*>(addr) = value;
	 }
      }
   }
   
   void VisualMachine::set_low_endian_value(uint32_t *addr, uint32_t value)
   {
      uint8_t *b = reinterpret_cast<uint8_t*>(addr);
      b[0] = static_cast<uint8_t>(value);
      b[1] = static_cast<uint8_t>(value >> 8);
      b[2] = static_cast<uint8_t>(value >> 16);
      b[3] = static_cast<uint8_t>(value >> 24);
   }

   uint32_t *VisualMachine::get_operand(VMPreparedOperand *cmdop)
   {
      if (cmdop->type == VMOpType::REGMEM)
	 return reinterpret_cast<uint32_t*>(mem + ((*cmdop->addr + cmdop->base) & VM_MEMMASK));
      else
	 return cmdop->addr;
   }
   
   void VisualMachine::execute(VMPreparedProgram *prg)
   {
      memcpy(R, prg->initr, sizeof(prg->initr));
      size_t globalsize = min(prg->globaldata.size(), VM_GLOBALMEMSIZE);
      if (globalsize != 0)
      {
	 memcpy(mem + VM_GLOBALMEMADDR, &prg->globaldata[0], globalsize);
      }
      size_t staticsize = min(prg->staticdata.size(), VM_GLOBALMEMSIZE - globalsize);
      if (staticsize != 0)
      {
	 memcpy(mem + VM_GLOBALMEMADDR + globalsize, &prg->staticdata[0], staticsize);
      }
      R[7] = VM_MEMSIZE;
      flags = 0;
      VMPreparedCommand *preparedcode = prg->altcmd ? prg->altcmd : &prg->cmd[0];
      if (prg->cmdcount > 0 && !execute_code(preparedcode, prg->cmdcount))
      {
	 preparedcode[0].opcode = VMCommand::RET;
      }
      uint32_t newblockposi = getvalue(false, reinterpret_cast<uint32_t*>(mem + VM_GLOBALMEMADDR + 0x20)) & VM_MEMMASK;
      uint32_t newblocksize = getvalue(false, reinterpret_cast<uint32_t*>(mem + VM_GLOBALMEMADDR + 0x1c)) & VM_MEMMASK;
      if (newblockposi + newblocksize >= VM_MEMSIZE)
      {
	 newblockposi = newblocksize = 0;
      }
      prg->filtereddata = mem + newblockposi;
      prg->filtereddatasize = newblocksize;
      dprint("po=%u\n", newblockposi);
      
      //dprint("prg->filtereddatasize=%d\tnewblocksize=%d\n", prg->filtereddatasize, newblocksize);
      prg->globaldata.clear();
      uint32_t datasize = min(getvalue(false, reinterpret_cast<uint32_t*>(mem + VM_GLOBALMEMADDR + 0x30)),
			      VM_GLOBALMEMSIZE - VM_FIXEDGLOBALSIZE);
      if (datasize != 0)
      {
	 prg->globaldata.assign(mem + VM_GLOBALMEMADDR, mem + VM_GLOBALMEMADDR + datasize + VM_FIXEDGLOBALSIZE);
      }
      //dprint("posi=%u\n", newblockposi);
      //dprint("%u\t%u\t", mem[newblockposi], *prg->filtereddata);
   }

/* This macro is only used for easy coding, 
 * it's should only be used in Fur::VisualMachine::execute_code */   
#define SET_IP(IP) \
   if (IP >= codesize) \
      return true;     \
   if (--maxopcount <= 0) \
      return false;	  \
   cmd = preparedcode + IP;

   bool VisualMachine::execute_code(VMPreparedCommand * preparedcode, uint32_t codesize)
   {
      uint32_t maxopcount = 25000000;
      VMPreparedCommand *cmd = preparedcode;
      while (true)
      {
	 uint32_t *op1 = get_operand(&cmd->op1);
	 uint32_t *op2 = get_operand(&cmd->op2);
	 switch(cmd->opcode)
	 {
	    case VMCommand::MOV:
	       setvalue(cmd->bytemode, op1, getvalue(cmd->bytemode, op2));
	    break;
	    case VMCommand::MOVB:
	       setvalue(true, op1, getvalue(true, op2));
	       break;
	    case VMCommand::MOVD:
	       setvalue(false, op1, getvalue(false, op2));
	       break;
	    case VMCommand::CMP:
	    {
	       uint32_t value1 = getvalue(true, op1);
	       uint32_t result = value1 - getvalue(true, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result > value1) | (result & VMFLAG_FS);
	    }
	    break;
	    case VMCommand::CMPB:
	    {
	       uint32_t value1 = getvalue(true, op1);
	       uint32_t result = value1 - getvalue(true, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result > value1) | (result & VMFLAG_FS);
	    }
	    break;
	    case VMCommand::CMPD:
	    {
	       uint32_t value1 = getvalue(false, op1);
	       uint32_t result = value1 - getvalue(false, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result > value1) | (result & VMFLAG_FS);
	    }
	    break;
	    case VMCommand::ADD:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t result = value1 + getvalue(cmd->bytemode, op2);
	       if (cmd->bytemode)
	       {
		  result &= 0xff;
		  flags = (result < value1) | (result == 0 ? VMFLAG_FZ : ((result & 0x80) ? VMFLAG_FS : 0));
	       }
	       else
		  flags = (result < value1) | (result == 0 ? VMFLAG_FZ : (result & VMFLAG_FS));
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::ADDB:
	       setvalue(true, op1, getvalue(true, op1) + getvalue(true, op2));
	       break;
	    case VMCommand::ADDD:
	       setvalue(false, op1, getvalue(false, op1) + getvalue(false, op2));
	       break;
	    case VMCommand::SUB:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t result = value1 - getvalue(cmd->bytemode, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result > value1) | (result & VMFLAG_FS);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::SUBB:
	       setvalue(true, op1, getvalue(true, op1) - getvalue(true, op2));
	       break;
	    case VMCommand::SUBD:
	       setvalue(false, op1, getvalue(false, op1) - getvalue(false, op2));
	       break;
	    case VMCommand::JZ:
	       if ((flags & VMFLAG_FZ) != 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::JNZ:
	       if ((flags & VMFLAG_FZ) == 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::INC:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) + 1;
	       if (cmd->bytemode)
		  result &= 0xff;
	       setvalue(cmd->bytemode, op1, result);
	       flags = (result == 0) ? VMFLAG_FZ :result & VMFLAG_FS;
	    }
	    break;
	    case VMCommand::INCB:
	       setvalue(true, op1, getvalue(true, op1) + 1);
	       break;
	    case VMCommand::INCD:
	       setvalue(false, op1, getvalue(false, op1) + 1);
	       break;
	    case VMCommand::DEC:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) - 1;
	       setvalue(cmd->bytemode, op1, result);
	       flags = (result == 0) ? VMFLAG_FZ : (result & VMFLAG_FS);
	    }
	    break;
	    case VMCommand::DECB:
	       setvalue(true, op1, getvalue(true, op1) - 1);
	       break;
	    case VMCommand::DECD:
	       setvalue(false, op1, getvalue(false, op1) - 1);
	       break;
	    case VMCommand::JMP:
	       SET_IP(getvalue(false, op1));
	       continue;
	    case VMCommand::XOR:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) ^ getvalue(cmd->bytemode, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result & VMFLAG_FS);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::AND:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) & getvalue(cmd->bytemode, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result & VMFLAG_FS);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::OR:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) | getvalue(cmd->bytemode, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result & VMFLAG_FS);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::TEST:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) & getvalue(cmd->bytemode, op2);
	       flags = (result == 0) ? VMFLAG_FZ : (result & VMFLAG_FS);
	    }
	    break;
	    case VMCommand::JS:
	       if ((flags & VMFLAG_FS) != 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::JNS:
	       if ((flags & VMFLAG_FS) == 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::JB:
	       if ((flags & VMFLAG_FC) != 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::JBE:
	       if ((flags & (VMFLAG_FC | VMFLAG_FZ)) != 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::JA:
	       if ((flags & VMFLAG_FC) == 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::JAE:
	       if ((flags & (VMFLAG_FC | VMFLAG_FZ)) == 0)
	       {
		  uint32_t ip = getvalue(false, op1);
		  SET_IP(ip);
		  continue;
	       }
	       break;
	    case VMCommand::PUSH:
	       R[7] -= 4;
	       setvalue(false, reinterpret_cast<uint32_t*>(mem + (R[7] & VM_MEMMASK)), getvalue(false, op1));
	       break;
	    case VMCommand::POP:
	       setvalue(false, op1, getvalue(false, reinterpret_cast<uint32_t*>(mem + R[7] + VM_MEMMASK)));
	       R[7] += 4;
	       break;
	    case VMCommand::CALL:
	    {
	       R[7] -= 4;
	       setvalue(false, reinterpret_cast<uint32_t*>(mem + R[7] + VM_MEMMASK), cmd - preparedcode + 1);
	       uint32_t ip = getvalue(false, op1);
	       SET_IP(ip);
	       continue;
	    }
	    case VMCommand::NOT:
	       setvalue(cmd->bytemode, op1, ~getvalue(cmd->bytemode, op1));
	       break;
	    case VMCommand::SHL:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t value2 = getvalue(cmd->bytemode, op2);
	       uint32_t result = value1 << value2;
	       flags = (result == 0 ? VMFLAG_FZ : (result & VMFLAG_FS)) | ((value1 << (value2 - 1)) & VMFLAG_FC);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::SHR:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t value2 = getvalue(cmd->bytemode, op2);
	       uint32_t result = value1 >> value2;
	       flags = (result == 0 ? VMFLAG_FZ : (result & VMFLAG_FS)) | ((value1 << (value2 - 1)) & VMFLAG_FC);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::SAR:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t value2 = getvalue(cmd->bytemode, op2);
	       uint32_t result = static_cast<int>(value1) >> value2;
	       flags = (result == 0 ? VMFLAG_FZ : (result & VMFLAG_FS)) | ((value1 >> (value2 - 1)) & VMFLAG_FC);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::NEG:
	    {
	       uint32_t result = 0 - getvalue(cmd->bytemode, op1);
	       flags = result == 0 ? VMFLAG_FZ : VMFLAG_FC | (result & VMFLAG_FS);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::NEGB:
	       setvalue(true, op1, 0 - getvalue(true, op1));
	       break;
	    case VMCommand::NEGD:
	       setvalue(false, op1, 0 - getvalue(false, op1));
	       break;
	    case VMCommand::PUSHA:
	    {
	       const int regcount = sizeof(R) / sizeof(R[0]);
	       int i = 0;
	       int sp = R[7] - 4;
	       for (; i < regcount; ++i)
	       {
		  setvalue(false, reinterpret_cast<uint32_t*>(mem + (sp & VM_MEMMASK)), R[i]);
		  sp -= 4;
	       }
	       R[7] -= regcount * 4;
	    }
	    break;
	    case VMCommand::POPA:
	    {
	       const int regcount = sizeof(R) / sizeof(R[0]);
	       int i = 0;
	       int sp = R[7];
	       for (; i < regcount; ++i)
	       {
		  R[7-i] = getvalue(false, reinterpret_cast<uint32_t*>(mem + (sp & VM_MEMMASK)));
		  sp += 4;
	       }
	    }
	    break;
	    case VMCommand::PUSHF:
	       R[7] -= 4;
	       setvalue(false, reinterpret_cast<uint32_t*>(mem + (R[7] & VM_MEMMASK)), flags);
	       break;
	    case VMCommand::POPF:
	       flags = getvalue(false, reinterpret_cast<uint32_t*>(mem + (R[7] & VM_MEMMASK)));
	       R[7] += 4;
	       break;
	    case VMCommand::MOVZX:
	       setvalue(false, op1, getvalue(true, op2));
	       break;
	    case VMCommand::MOVSX:
	       setvalue(false, op1, static_cast<signed char>(getvalue(true, op2)));
	       break;
	    case VMCommand::XCHG:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       setvalue(cmd->bytemode, op1, getvalue(cmd->bytemode, op2));
	       setvalue(cmd->bytemode, op2, value1);
	    }
	    break;
	    case VMCommand::MUL:
	    {
	       uint32_t result = getvalue(cmd->bytemode, op1) * getvalue(cmd->bytemode, op2);
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::DIV:
	    {
	       uint32_t divider = getvalue(cmd->bytemode, op1);
	       if (divider != 0)
	       {
		  uint32_t result = getvalue(cmd->bytemode, op1) / divider;
		  setvalue(cmd->bytemode, op1, result);
	       }
	    }
	    break;
	    case VMCommand::ADC:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t fc = (flags & VMFLAG_FC);
	       uint32_t result = value1 + getvalue(cmd->bytemode, op2) + fc;
	       if (cmd->bytemode)
	       {
		  result &= 0xff;
	       }
	       flags = (result < value1 || (result == value1 && fc)) | (result == 0 ? VMFLAG_FZ : (result & VMFLAG_FS));
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::SBB:
	    {
	       uint32_t value1 = getvalue(cmd->bytemode, op1);
	       uint32_t fc = (flags & VMFLAG_FC);
	       uint32_t result = value1 - getvalue(cmd->bytemode, op2) - fc;
	       if (cmd->bytemode)
	       {
		  result &= 0xff;
	       }
	       flags = (result > value1 || (result == value1 && fc)) | (result == 0 ? VMFLAG_FZ : (result & VMFLAG_FS));
	       setvalue(cmd->bytemode, op1, result);
	    }
	    break;
	    case VMCommand::RET:
	    {
	       if (R[7] >= VM_MEMSIZE)
		  return true;
	       uint32_t ip = getvalue(false, reinterpret_cast<uint32_t*>(mem + (R[7] + VM_MEMMASK)));
	       SET_IP(ip);
	       R[7] += 4;
	       continue;
	    }
	    case VMCommand::STANDARD:
	       execute_standard_filter(VMStandardFilter(cmd->op1.data));
	       break;
	    case VMCommand::PRINT:
	       break;
	 }
	 ++cmd;
	 --maxopcount;
      }
   }
   
   void  VisualMachine::prepare(uint8_t *code, size_t codesize, VMPreparedProgram *prg)
   {
      reset();
      memcpy(data, code, min(codesize, default_bitinput_buffsize));
      uint8_t xorsum = 0;
      for (size_t i =  1; i < codesize; ++i)
	 xorsum ^= code[i];
      addbits(8);
      prg->cmdcount = 0;
      if (xorsum == code[0])
      {
	 VMStandardFilter filtertype = is_standard_filter(code, codesize);
	 if (filtertype != VMStandardFilter::NONE)
	 {
	    prg->cmd.push_back(VMPreparedCommand());
	    VMPreparedCommand &curcmd = prg->cmd[prg->cmdcount++];
	    curcmd.opcode = VMCommand::STANDARD;
	    curcmd.op1.data = static_cast<uint32_t>(filtertype);
	    curcmd.op1.addr = &curcmd.op1.data;
	    curcmd.op2.addr = &curcmd.op2.data;
	    curcmd.op1.type = curcmd.op2.type = VMOpType::NONE;
	    codesize = 0;
	 }
	 uint16_t dataflag = getbits_16();
	 addbits(1);
	 if (dataflag & 0x8000)
	 {
	    uint32_t datasize = vm_read_data(*this) + 1;
	    assert(prg->staticdata.size() == 0);
	    for (uint32_t i = 0; byteposi < codesize && i < datasize; ++i)
	    {
	       prg->staticdata.push_back(getbits_8());
	       addbits(8);
	    }
	 }
	 
	 while (byteposi < codesize)
	 {
	    prg->cmd.push_back(VMPreparedCommand());
	    VMPreparedCommand &curcmd = prg->cmd[prg->cmdcount++];
	    uint16_t data = getbits_16();
	    if ((data & 0x8000) == 0)
	    {
	       curcmd.opcode = static_cast<VMCommand>(data >> 12);
	       addbits(4);
	    }
	    else
	    {
	       curcmd.opcode = static_cast<VMCommand>((data >> 10) - 24);
	       addbits(6);
	    }
	    if (vm_cmd_flags[static_cast<uint32_t>(curcmd.opcode)] & VMCmdFlag::BYTEMODE)
	    {
	       curcmd.bytemode = (getbits_8()>>7) != 0;
	       addbits(1);
	    }
	    else
	    {
	       curcmd.bytemode = 0;
	    }
	    curcmd.op1.type = curcmd.op2.type = VMOpType::NONE;
	    int opnum = (vm_cmd_flags[static_cast<uint32_t>(curcmd.opcode)] & VMCmdFlag::OPMASK);
	    curcmd.op1.addr = curcmd.op2.addr = NULL;
	    if (opnum > 0)
	    {
	       decode_arg(curcmd.op1, curcmd.bytemode);
	       if (opnum == 2)
		  decode_arg(curcmd.op2, curcmd.bytemode);
	       else
	       {
		  if (curcmd.op1.type == VMOpType::INT && (vm_cmd_flags[static_cast<uint32_t>(curcmd.opcode)] & (VMCmdFlag::JUMP | VMCmdFlag::PROC)))
		  {
		     int distance = curcmd.op1.data;
		     if (distance >= 256)
		     {
			distance -= 256;
		     }
		     else if (distance >=136)
		     {
			distance -= 264;
		     }
		     else if (distance >= 16)
		     {
			distance -= 8;
		     }
		     else if (distance >= 8)
		     {
			distance -=16;
		     }
		     distance += prg->cmdcount;
		  }
	       }
	    } // if (opnum > 0) { ...
	    ++prg->cmdcount;
	 } // while (byteposi < codesize) { ...
      } // if (xorsum == code[0]) { ...
      prg->cmd.push_back(VMPreparedCommand());
      VMPreparedCommand &curcmd = prg->cmd[prg->cmdcount++];
      curcmd.opcode = VMCommand::RET;
      curcmd.op1.addr = &curcmd.op1.data;
      curcmd.op2.addr = &curcmd.op2.data;
      curcmd.op1.type = curcmd.op2.type = VMOpType::NONE;
      assert(prg->cmd.size() == prg->cmdcount);
      for (uint32_t i = 0; i < prg->cmd.size(); ++i)
      {
	 VMPreparedCommand &tmpcmd = prg->cmd[i];
	 if (tmpcmd.op1.addr == NULL)
	 {
	    tmpcmd.op1.addr = &tmpcmd.op1.data;
	 }
	 if (tmpcmd.op2.addr == NULL)
	 {
	    tmpcmd.op2.addr = &tmpcmd.op2.data;
	 }
	 
      }
/*
if (codesize != 0)
optimize(prg);
*/
   }
   
   
   void VisualMachine::decode_arg(VMPreparedOperand &op, bool bytemode)
   {
      uint32_t data = getbits_16();
      if (data &0x8000)
      {
	 op.type = VMOpType::REG;
	 op.data = (data >> 12) & 7;
	 op.addr = &R[op.data];
	 addbits(4);
      }
      else if ((data & 0xc000) == 0)
      {
	 op.type = VMOpType::INT;
	 if (bytemode)
	 {
	    op.data = (data >> 6) &0xff;
	    addbits(10);
	 }
	 else
	 {
	    addbits(2);
	    op.data = vm_read_data(*this);
	 }
      }
      else
      {
	 op.type = VMOpType::REGMEM;
	 if ((data & 0x2000) == 0)
	 {
	    op.data = (data >> 10) & 7;
	    op.addr = &R[op.data];
	    op.base = 0;
	    addbits(6);
	 }
	 else
	 {
	    if ((data & 0x1000) == 0)
	    {
	       op.data = (data >> 9) & 7;
	       op.addr = &R[op.data];
	       addbits(7);
	    }
	    else
	    {
	       op.data = 0;
	       addbits(4);
	    }
	    op.base = vm_read_data(*this);
	 }
      }
   }
   
   uint32_t vm_read_data(BitInputBase &bitinput)
   {
      uint32_t data = bitinput.getbits_16();
      switch (data & 0xc000)
      {
	 case 0:
	    bitinput.addbits(6);
	    return (data>>10) & 0xf;
	 case 0x4000:
	    if ((data & 0x3c00) == 0)
	    {
	       data = 0xffffff00 | ((data >> 2) & 0xff);
	       bitinput.addbits(14);
	    }
	    else
	    {
	       data = (data >> 6) & 0xff;
	       bitinput.addbits(10);
	    }
	    return data;
	 case 0x8000:
	    bitinput.addbits(2);
	    data = bitinput.getbits_16();
	    bitinput.addbits(16);
	    return data;
	 default:
	    bitinput.addbits(2);
	    data = (bitinput.getbits_16() << 16);
	    bitinput.addbits(165);
	    data |= bitinput.getbits_16();
	    bitinput.addbits(16);
	    return data;
      }
   }

   VMStandardFilter VisualMachine::is_standard_filter(uint8_t *code, uint32_t codesize)
   {
      class SFSign
      {
      public:
	 size_t length;
	 uint32_t crc;
	 VMStandardFilter type;
      };
      static SFSign stdlist[] = 
	 {
	    {53, 0xad576887, VMStandardFilter::E8},
	    {57, 0x3cd7e57e, VMStandardFilter::E8E9},
	    {120, 0x3769893f, VMStandardFilter::ITANIUM},
	    {29, 0x0e06077d, VMStandardFilter::DELTA},
	    {149, 0x1c2c5dc8, VMStandardFilter::RGB},
	    {216, 0xbc85e701, VMStandardFilter::AUDIO},
	    {40, 0x46b9c560, VMStandardFilter::UPCASE}
	 };
      uint32_t codecrc = boost::crc<32, 0x4c11db7, 0xffffffff, 0, true, true>(code, codesize) ^ 0xffffffff;
      for (uint32_t i = 0; i < sizeof(stdlist) / sizeof(stdlist[0]); ++i)
      {
	 if (stdlist[i].crc == codecrc && stdlist[i].length == codesize)
	 {
	    return stdlist[i].type;
	 }
      }
      return VMStandardFilter::NONE;
   }

   void VisualMachine::execute_standard_filter(VMStandardFilter filtertype)
   {
      switch(filtertype)
      {
	 case VMStandardFilter::NONE:
	    break;
	 case VMStandardFilter::E8:
	 case VMStandardFilter::E8E9:
	 {
	    uint8_t *tmpmem = mem;
	    uint32_t datasize = R[4];
	    uint32_t fileoffset = R[6];
	    if (datasize >= VM_GLOBALMEMADDR || datasize < 4)
	    {
	       break;
	    }
	    const int filesize = 0x1000000;
	    uint8_t cmpbyte2 = (filtertype == VMStandardFilter::E8E9) ? 0xe9 : 0xe8;
	    for (uint32_t curpos = 0; curpos < datasize - 4; )
	    {
	       uint8_t curbyte = *tmpmem;
	       ++tmpmem;
	       ++curpos;
	       if (curbyte == 0xe8 || curbyte == cmpbyte2)
	       {
		  int32_t offset = curpos + fileoffset;
		  int32_t addr = getvalue(false, reinterpret_cast<uint32_t*>(tmpmem));
		  if (addr < 0)
		  {
		     if (addr + offset >= 0)
		     {
			setvalue(false, reinterpret_cast<uint32_t*>(tmpmem), addr + filesize);
		     }
		  }
		  else
		  {
		     if (addr < filesize)
		     {
			setvalue(false, reinterpret_cast<uint32_t*>(tmpmem), addr - offset);
		     }
		  }
		  tmpmem += 4;
		  curpos += 4;
	       }
	    }
	 }
	 break;
	 case VMStandardFilter::ITANIUM:
	 case VMStandardFilter::RGB:
	 case VMStandardFilter::AUDIO:
	    throw EUnsupport(__func__);
	 case VMStandardFilter::DELTA:
	 {
	    uint32_t datasize = R[4];
	    uint32_t channels = R[0];
	    uint32_t srcpos = 0;
	    uint32_t border = datasize * 2;
	    setvalue(false, reinterpret_cast<uint32_t*>(mem + VM_GLOBALMEMADDR + 0x20), datasize);
	    if (datasize >= VM_GLOBALMEMADDR / 2)
	    {
	       break;
	    }
	    for (uint32_t curchannel = 0; curchannel < channels; ++curchannel)
	    {
	       uint8_t prevbyte = 0;
	       for (uint32_t destpos = datasize + curchannel; destpos < border; destpos += channels)
	       {
		  prevbyte -= mem[srcpos];
		  ++srcpos;
		  mem[destpos] = prevbyte;
	       }
	    }
	 }
	 break;
	 case VMStandardFilter::UPCASE:
	 {
	    uint32_t datasize = R[4];
	    uint32_t srcpos = 0;
	    uint32_t destpos = datasize;
	    if (datasize >= VM_GLOBALMEMADDR / 2)
	    {
	       break;
	    }
	    while (srcpos < datasize)
	    {
	       uint8_t curbyte = mem[srcpos];
	       ++srcpos;
	       if (curbyte == 2)
	       {
		  curbyte = mem[srcpos++];
		  if (curbyte != 2)
		  {
		     curbyte -= 32;
		  }
	       }
	       mem[destpos] = curbyte;
	       ++destpos;
	    }
	    setvalue(false, reinterpret_cast<uint32_t*>(mem + VM_GLOBALMEMADDR + 0x1c), destpos - datasize);
	    setvalue(false, reinterpret_cast<uint32_t*>(mem + VM_GLOBALMEMADDR + 0x20), datasize);
	 }
	 break;
      } // switch (filtertype) { ...
   }
   
   void VisualMachine::set_memory(uint32_t posi, uint8_t *adata, uint32_t value)
   {
      if (posi < VM_MEMSIZE && adata != mem + posi)
      {
	 memmove(mem + posi, adata, min(value, VM_MEMSIZE - posi));
      }
   }

} // end of namespace fur
